#include "tcp_sender.hh"

#include "tcp_config.hh"

#include <random>

// Dummy implementation of a TCP sender

// For Lab 3, please replace with a real implementation that passes the
// automated checks run by `make check_lab3`.

using namespace std;

//! \param[in] capacity the capacity of the outgoing byte stream
//! \param[in] retx_timeout the initial amount of time to wait before retransmitting the oldest outstanding segment
//! \param[in] fixed_isn the Initial Sequence Number to use, if set (otherwise uses a random ISN)
TCPSender::TCPSender(const size_t capacity, const uint16_t retx_timeout, const std::optional<WrappingInt32> fixed_isn)
    : _isn(fixed_isn.value_or(WrappingInt32{random_device()()}))
    , _initial_retransmission_timeout{retx_timeout}
    , _retransmission_timeout{retx_timeout}
    , _stream(capacity) {}

uint64_t TCPSender::bytes_in_flight() const {
    return _bytes_in_flight;
}

void TCPSender::fill_window() {
    if (_fin)
        return;
    while (_bytes_in_flight < _window_size) {
        TCPSegment seg;
        if (!_syn) {
            _syn = true;
            seg.header().syn = true;
        }
        size_t len = min(_window_size - _bytes_in_flight - seg.length_in_sequence_space(), TCPConfig::MAX_PAYLOAD_SIZE);
        seg.payload() = _stream.read(len);
        seg.header().seqno = wrap(_next_seqno, _isn);
        if (!_fin && _stream.eof()) {
            if (bytes_in_flight() + seg.length_in_sequence_space() < _window_size) {
                seg.header().fin = true;
                _fin = true;
            }
        }
        if (seg.length_in_sequence_space() > 0) {
            _copy_segments_out.push(seg);
            _segments_out.push(seg);
            _next_seqno += seg.length_in_sequence_space();
            _bytes_in_flight += seg.length_in_sequence_space();
            if (!_clock_running) {
                _clock_running = true;
                _cur_time = 0;
            }
        } else {
            break;
        }
    }
}

//! \param ackno The remote receiver's ackno (acknowledgment number)
//! \param window_size The remote receiver's advertised window size
void TCPSender::ack_received(const WrappingInt32 ackno, const uint16_t window_size) {
    if (next_seqno().raw_value() < ackno.raw_value())
        return;
    bool ok = false;
    while (!_copy_segments_out.empty()) {
        TCPSegment seg = _copy_segments_out.front();     
        if (seg.header().seqno.raw_value() + seg.length_in_sequence_space()  > ackno.raw_value())
            break;
        ok = true;
        _bytes_in_flight -= seg.length_in_sequence_space();
        _copy_segments_out.pop();
        _window_size = window_size;
    }
    // 如果有多余的空间, fill_window函数会被调用继续填充空间
    // 如果所有的报文段都被确定了, 关闭计时器并且重置时间
    if (_copy_segments_out.size() == 0) {
        _clock_running = false;
        _retransmission_timeout = _initial_retransmission_timeout;
         _consecutive_retransmissions = 0;
        _cur_time = 0;
    } else if (ok) {  
        _clock_running = true;
        _retransmission_timeout = _initial_retransmission_timeout;
        _consecutive_retransmissions = 0;
        _cur_time = 0;
    }
    // 如果是有效的ack，改变窗口大小
    _window_size = window_size;
    if (_window_size == 0) {
        _window_size = 1;
        _zero = true;
    } else {
        _zero = false;
    }
}

//! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method
void TCPSender::tick(const size_t ms_since_last_tick) { 
    if (!_clock_running)
        return;
    _cur_time += ms_since_last_tick;
    // 如果到了重传时间
    if (_cur_time >= _retransmission_timeout) {
        if (_copy_segments_out.size() > 0 && _consecutive_retransmissions < TCPConfig::MAX_RETX_ATTEMPTS) {
            // 重传报文段
            TCPSegment seg = _copy_segments_out.front();
            _segments_out.push(seg);
        }
        // 如果窗口大小大于0
        if (!_zero) {
            _consecutive_retransmissions++;
            _retransmission_timeout *= 2;
        }
        _clock_running = true;
        _cur_time = 0;
    }
}

unsigned int TCPSender::consecutive_retransmissions() const { 
    return _consecutive_retransmissions;
}

void TCPSender::send_empty_segment() {
    TCPSegment seg;
    seg.header().seqno = wrap(_next_seqno, _isn);
    _segments_out.push(seg);
}
